
#include <BEvent>
#include <BLayout>
#include "member_BLayout.h"
#include "member_BWidget.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BLayout*)_ptr)
#define member_allocate()		_ptr = new member_BLayout(this)
#define member_release()		delete (member_BLayout*)_ptr

BLayout::BLayout(BWidget* host)
{
	member_allocate();
	if (host)
		host->setLayout(this, Part_Center);
}
BLayout::BLayout(BWidget* host, PartFlag part)
{
	member_allocate();
	if (host)
		host->setLayout(this, part);
}
BLayout::BLayout(const BString& name, BWidget* host) : BObject(name)
{
	member_allocate();
	if (host)
		host->setLayout(this, Part_Center);
}
BLayout::BLayout(const BString& name, BWidget* host, PartFlag part) : BObject(name)
{
	member_allocate();
	if (host)
		host->setLayout(this, part);
}
BLayout::~BLayout()
{
	member_release();
}

bool BLayout::setEnabled(bool enabled)
{
	if (BObject::setEnabled(enabled))
	{
		member.dirty = true;
		if (member.parent)
		{
			member.parent->dirty(this);
			member.parent->dirty();
		}
		if (member.host)
		{
			member.host->adjust();
			member.host->fresh();
			bool enabled = widget_member(member.host)->state.enabled;
			widget_member(member.host)->freshEnabled(enabled);
		}
		emit(Signal_Dirty);
		return true;
	}
	return false;
}

void BLayout::fresh() const
{
	if (member.host)
	{
		member.host->fresh();
	}
}
void BLayout::dirty()
{
	if (member.dirty)
	{
		const BLayout* layout = this;
		while (layout_member(layout)->parent)
		{
			layout_member(layout)->parent->dirty(layout);
			layout = layout_member(layout)->parent;
		}
		if (BWidget* host = member.host)
		{
			widget_member(host)->dirtyLayouts = true;
		}
	}
	else
	{
		member.dirty = true;
		emit(Signal_Dirty);
		if (member.parent)
		{
			member.parent->dirty(this);
			member.parent->dirty();
		}
		else if (member.host)
		{
			member.host->adjust();
			member.host->fresh();
		}
	}
}

BWidget* BLayout::host()
{
	return member.host;
}
const BWidget* BLayout::host() const
{
	return member.host;
}

BLayout* BLayout::parent()
{
	return member.parent;
}
const BLayout* BLayout::parent() const
{
	return member.parent;
}

void BLayout::setPart(PartFlag part)
{
	if (member.part != part)
	{
		member.part = part;
		this->dirty();
	}
}
PartFlag BLayout::part() const
{
	return member.part;
}

void BLayout::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (member.visible)
			this->emit(Signal_Show, true);
		else
			this->emit(Signal_Hide, false);
		this->dirty();
	}
}
bool BLayout::visible() const
{
	return member.visible;
}

BStyle* BLayout::style() const
{
	return member.realStyle;
}

void BLayout::setMargin(int left, int top, int right, int bottom)
{
	setMargin(BMargin(left, top, right, bottom));
}
void BLayout::setMargin(const BMargin& margin)
{
	if (member.margin != margin)
	{
		member.margin = margin;
		this->dirty();
	}
}
const BMargin& BLayout::margin() const
{
	return member.margin;
}

void BLayout::setMargin(Part part, int value)
{
	BMargin margin = member.margin;
	if (part & Part_Left)
		margin.left() = value;
	if (part & Part_Top)
		margin.top() = value;
	if (part & Part_Right)
		margin.right() = value;
	if (part & Part_Bottom)
		margin.bottom() = value;
	this->setMargin(margin);
}
int BLayout::margin(Part part) const
{
	switch (part)
	{
	case Part_Left:
		return member.margin.left();
	case Part_Top:
		return member.margin.top();
	case Part_Right:
		return member.margin.right();
	case Part_Bottom:
		return member.margin.bottom();
	default:
		return -1;
	}
}

void BLayout::setWidthPolicy(Policy widthPolicy)
{
	setSizePolicy(BSizePolicy(widthPolicy, member.sizePolicy.heightPolicy()));
}
Policy BLayout::widthPolicy() const
{
	return sizePolicy().widthPolicy();
}

void BLayout::setHeightPolicy(Policy heightPolicy)
{
	setSizePolicy(BSizePolicy(member.sizePolicy.widthPolicy(), heightPolicy));
}
Policy BLayout::heightPolicy() const
{
	return sizePolicy().heightPolicy();
}

void BLayout::setSizePolicy(Policy widthPolicy, Policy heightPolicy)
{
	setSizePolicy(BSizePolicy(widthPolicy, heightPolicy));
}
void BLayout::setSizePolicy(const BSizePolicy& sizePolicy)
{
	if (member.sizePolicy != sizePolicy)
	{
		member.sizePolicy = sizePolicy;
		this->dirty();
	}
}
BSizePolicy BLayout::sizePolicy() const
{
	BSizePolicy policy = member.sizePolicy;
	if (policy.widthPolicy() == Policy_None)
	{
		ILayoutItem& item = member.item;
		if (item.dirty)
			item.flush();
		if (!item.ignored)
			policy.widthPolicy() |= item.sizePolicy.widthPolicy();
	}
	if (policy.heightPolicy() == Policy_None)
	{
		ILayoutItem& item = member.item;
		if (item.dirty)
			item.flush();
		if (!item.ignored)
			policy.heightPolicy() |= item.sizePolicy.heightPolicy();
	}
	return policy;
}

void BLayout::setFixedSize(int fixedWidth, int fixedHeight)
{
	setFixedSize(BSize(fixedWidth, fixedHeight));
}
void BLayout::setFixedSize(const BSize& fixedSize)
{
	if (member.fixedSize != fixedSize)
	{
		member.fixedSize = fixedSize;
		this->dirty();
	}
}
BSize BLayout::fixedSize() const
{
	BSize fixedSize = layout_member(this)->fixedSize;
	if (fixedSize.width() == 0 || fixedSize.height() == 0)
	{
		const BMargin& margin = this->margin();
		if (member.item.dirty)
			member.item.flush();
		if (fixedSize.width() == 0)
			fixedSize.width() = member.item.fixedSize.width() + margin.left() + margin.right();
		if (fixedSize.height() == 0)
			fixedSize.height() = member.item.fixedSize.height() + margin.top() + margin.bottom();
	}
	return fixedSize;
}

void BLayout::setMinSize(int minWidth, int minHeight)
{
	setMinSize(BSize(minWidth, minHeight));
}
void BLayout::setMinSize(const BSize& minSize)
{
	if (member.minSize != minSize)
	{
		member.minSize = minSize;
		this->dirty();
	}
}
BSize BLayout::minSize() const
{
	return member.minSize;
}

void BLayout::setMinWidth(int minWidth)
{
	if (member.minSize.width() != minWidth)
	{
		member.minSize.width() = minWidth;
		this->dirty();
	}
}
int BLayout::minWidth() const
{
	return member.minSize.width();
}

void BLayout::setMinHeight(int minHeight)
{
	if (member.minSize.height() != minHeight)
	{
		member.minSize.height() = minHeight;
		this->dirty();
	}
}
int BLayout::minHeight() const
{
	return member.minSize.height();
}

void BLayout::setMaxSize(int maxWidth, int maxHeight)
{
	setMaxSize(BSize(maxWidth, maxHeight));
}
void BLayout::setMaxSize(const BSize& maxSize)
{
	if (member.maxSize != maxSize)
	{
		member.maxSize = maxSize;
		this->dirty();
	}
}
BSize BLayout::maxSize() const
{
	return member.maxSize;
}

void BLayout::setMaxWidth(int maxWidth)
{
	if (member.maxSize.width() != maxWidth)
	{
		member.maxSize.width() = maxWidth;
		this->dirty();
	}
}
int BLayout::maxWidth() const
{
	return member.maxSize.width();
}

void BLayout::setMaxHeight(int maxHeight)
{
	if (member.maxSize.height() != maxHeight)
	{
		member.maxSize.height() = maxHeight;
		this->dirty();
	}
}
int BLayout::maxHeight() const
{
	return member.maxSize.height();
}

BSize BLayout::preferSize() const
{
	BSizePolicy policy = this->sizePolicy();
	BSize baseSize = this->baseSize();
	BSize hintSize = this->sizeHint();
	BSize fixedSize = this->fixedSize();
	BSize minSize = this->minSize();
	BSize maxSize = this->maxSize();

	if (policy.widthPolicy() == Policy_Fixed)
		baseSize.width() = fixedSize.width();
	else if (policy.widthPolicy() & Policy_Preferred)
		baseSize.width() = hintSize.width();
	
	if (policy.heightPolicy() == Policy_Fixed)
		baseSize.height() = fixedSize.height();
	else if (policy.heightPolicy() & Policy_Preferred)
		baseSize.height() = hintSize.height();

	baseSize.limit(minSize, maxSize);
	return baseSize;
}

bool BLayout::setRect(int x, int y, int width, int height)
{
	return setRect(BRect(x, y, width, height));
}
bool BLayout::setRect(const BPoint& pos, const BSize& size)
{
	return setRect(BRect(pos, size));
}
bool BLayout::setRect(const BRect& rect)
{
	if (member.rect != rect)
	{
		bool moved = member.rect.pos() != rect.pos();
		bool resized = member.rect.size() != rect.size();
		member.rect = rect;
		member.dirty = true;
		if (moved)
			this->emit(Signal_Moved, rect.pos());
		if (resized)
			this->emit(Signal_Resized, rect.size());
		return true;
	}
	return false;
}
const BRect& BLayout::rect() const
{
	return member.rect;
}
BRect BLayout::rect(Part part) const
{
	int width = member.rect.width();
	int height = member.rect.height();
	int top = member.margin.top();
	int left = member.margin.left();
	int right = member.margin.right();
	int bottom = member.margin.bottom();
	if (part == Part_Top)
	{
		BRect rectTop(left, 0, width - left - right, top);
		return rectTop + member.rect.pos();
	}
	if (part == Part_Right)
	{
		BRect rectRight(width - right, top, right, height - top - bottom);
		return rectRight + member.rect.pos();
	}
	if (part == Part_Left)
	{
		BRect rectLeft(0, top, left, height - top - bottom);
		return rectLeft + member.rect.pos();
	}
	if (part == Part_Bottom)
	{
		BRect rectBottom(left, height - bottom, width - left - right, bottom);
		return rectBottom + member.rect.pos();
	}
	if (part == Part_LeftTop)
	{
		BRect rectLeftTop(0, 0, left, top);
		return rectLeftTop + member.rect.pos();
	}
	if (part == Part_LeftBottom)
	{
		BRect rectLeftBottom(0, height - bottom, left, bottom);
		return rectLeftBottom + member.rect.pos();
	}
	if (part == Part_RightTop)
	{
		BRect rectRightTop(width - right, 0, right, top);
		return rectRightTop + member.rect.pos();
	}
	if (part == Part_RightBottom)
	{
		BRect rectRightBottom(width - right, height - bottom, right, bottom);
		return rectRightBottom + member.rect.pos();
	}
	if (part == Part_Center)
	{
		BRect rectCenter(left, top, width - left - right, height - top - bottom);
		return rectCenter;
	}
	return BRect();
}

bool BLayout::setAlign(Align align)
{
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}
Align BLayout::align() const
{
	return member.item.align;
}

bool BLayout::setWidget(BWidget* widget, Align align)
{
	if (member.item != widget)
	{
		member.item.clear();
		member.item.widget = widget;
		member.item.align = align;
		member.item.dirty = true;
		if (widget)
		{
			if (widget_member(widget)->upperLayout)
				widget_member(widget)->upperLayout->remove(widget);
			widget_member(widget)->upperLayout = this;
			if (member.host)
				member.host->addChild(widget);
		}
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		member.item.dirty = true;
		this->dirty();
	}
	return true;
}
const BWidget* BLayout::widget() const
{
	return member.item.widget;
}
BWidget* BLayout::widget()
{
	return member.item.widget;
}

bool BLayout::setLayout(BLayout* layout, Align align)
{
	if (member.item != layout)
	{
		member.item.clear();
		member.item.layout = layout;
		member.item.align = align;
		if (layout)
		{
			if (layout_member(layout)->parent)
				layout_member(layout)->parent->remove(layout);
			layout_member(layout)->parent = this;
			layout_member(layout)->setHost(member.host);
		}
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}
const BLayout* BLayout::layout() const
{
	return member.item.layout;
}
BLayout* BLayout::layout()
{
	return member.item.layout;
}

bool BLayout::setSpacer(BSpacer* spacer, Align align)
{
	if (member.item != spacer)
	{
		member.item.clear();
		member.item.spacer = spacer;
		member.item.align = align;
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}
const BSpacer* BLayout::spacer() const
{
	return member.item.spacer;
}
BSpacer* BLayout::spacer()
{
	return member.item.spacer;
}

bool BLayout::dirty(const BObject* object)
{
	if (member.item == object)
	{
		member.item.dirty = true;
		this->dirty();
		return true;
	}
	return false;
}
Align BLayout::align(const BObject* object) const
{
	if (member.item == object)
	{
		return member.item.align;
	}
	return BLayout::align(object);
}
const BRect& BLayout::area(const BObject* object) const
{
	if (member.item == object)
	{
		return member.item.rect;
	}
	return member.rect;
}

BObject* BLayout::find(const BString& name) const
{
	if (BWidget* widget = member.item.widget)
	{
		if (widget->name() == name)
			return widget;
	}
	if (BSpacer* spacer = member.item.spacer)
	{
		if (spacer->name() == name)
			return spacer;
	}
	if (BSpring* spring = member.item.spring)
	{
		if (spring->name() == name)
			return spring;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->name() == name)
			return layout;
		BObject* obj = layout->find(name);
		if (obj)
			return obj;
	}
	return 0;
}
BWidget* BLayout::findWidget(const BString& name) const
{
	if (BWidget* widget = member.item.widget)
	{
		if (widget->name() == name)
			return widget;
	}
	if (BLayout* layout = member.item.layout)
	{
		BWidget* widget = layout->findWidget(name);
		if (widget)
			return widget;
	}
	return 0;
}
BLayout* BLayout::findLayout(const BString& name) const
{
	if (BLayout* layout = member.item.layout)
	{
		if (layout->name() == name)
			return layout;
		BLayout* lay = layout->findLayout(name);
		if (lay)
			return lay;
	}
	return 0;
}
BSpacer* BLayout::findSpacer(const BString& name) const
{
	if (BSpacer* spacer = member.item.spacer)
	{
		if (spacer->name() == name)
			return spacer;
	}
	if (BLayout* layout = member.item.layout)
	{
		BSpacer* spacer = layout->findSpacer(name);
		if (spacer)
			return spacer;
	}
	return 0;
}
BSpring* BLayout::findSpring(const BString& name) const
{
	if (BSpring* spring = member.item.spring)
	{
		if (spring->name() == name)
			return spring;
	}
	if (BLayout* layout = member.item.layout)
	{
		BSpring* spring = layout->findSpring(name);
		if (spring)
			return spring;
	}
	return 0;
}

bool BLayout::contain(const BObject* object) const
{
	if (object)
	{
		if (const BWidget* widget = dynamic_cast<const BWidget*>(object))
			return contain(widget);
		if (const BLayout* layout = dynamic_cast<const BLayout*>(object))
			return contain(layout);
		if (const BSpring* spring = dynamic_cast<const BSpring*>(object))
			return contain(spring);
		if (const BSpacer* spacer = dynamic_cast<const BSpacer*>(object))
			return contain(spacer);
	}
	return false;
}
bool BLayout::contain(const BWidget* widget) const
{
	if (member.item.widget)
	{
		if (member.item.widget == widget)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(widget))
			return true;
	}
	return false;
}
bool BLayout::contain(const BLayout* layout) const
{
	if (member.item.layout)
	{
		if (member.item.layout == layout)
			return true;
		if (member.item.layout->contain(layout))
			return true;
	}
	return false;
}
bool BLayout::contain(const BSpacer* spacer) const
{
	if (member.item.spacer)
	{
		if (member.item.spacer == spacer)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(spacer))
			return true;
	}
	return false;
}
bool BLayout::contain(const BSpring* spring) const
{
	if (member.item.spring)
	{
		if (member.item.spring == spring)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(spring))
			return true;
	}
	return false;
}

bool BLayout::remove(BObject* object)
{
	if (object)
	{
		if (BWidget* widget = dynamic_cast<BWidget*>(object))
			return remove(widget);
		if (BLayout* layout = dynamic_cast<BLayout*>(object))
			return remove(layout);
		if (BSpring* spring = dynamic_cast<BSpring*>(object))
			return remove(spring);
		if (BSpacer* spacer = dynamic_cast<BSpacer*>(object))
			return remove(spacer);
	}
	return false;
}
bool BLayout::remove(BWidget* widget)
{
	if (member.item.widget)
	{
		if (member.item.widget == widget)
		{
			widget_member(widget)->upperLayout = 0;
			member.item.widget = 0;
			if (BWidget* host = layout_member(this)->host)
				host->removeChild(widget);
			this->dirty();
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(widget))
			return true;
	}
	return false;
}
bool BLayout::remove(BLayout* layout)
{
	if (member.item.layout)
	{
		if (member.item.layout == layout)
		{
			if (BWidget* host = this->host())
				widget_member(host)->takeLayout(layout);
			member.item.layout = 0;
			this->dirty();
			return true;
		}
		if (member.item.layout->remove(layout))
			return true;
	}
	return false;
}
bool BLayout::remove(BSpacer* spacer)
{
	if (member.item.spacer)
	{
		if (member.item.spacer == spacer)
		{
			member.item.spacer = 0;
			this->dirty();
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(spacer))
			return true;
	}
	return false;
}
bool BLayout::remove(BSpring* spring)
{
	if (member.item.spring)
	{
		if (member.item.spring == spring)
		{
			member.item.spring = 0;
			this->dirty();
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(spring))
			return true;
	}
	return false;
}
bool BLayout::clear()
{
	if (member.item.empty())
		return false;
	if (BWidget* widget = member.item.widget)
	{
		return remove(widget);
	}
	if (BLayout* layout = member.item.layout)
	{
		return remove(layout);
	}
	if (BSpacer* spacer = member.item.spacer)
	{
		return remove(spacer);
	}
	if (BSpring* spring = member.item.spring)
	{
		return remove(spring);
	}
	return false;
}

int BLayout::count() const
{
	return 1;
}
BObject* BLayout::object(int index) const
{
	if (index == 0)
	{
		ILayoutItem& item = member.item;
		if (item.widget)
			return item.widget;
		if (item.layout)
			return item.layout;
		if (item.spacer)
			return item.spacer;
		if (item.spring)
			return item.spring;
	}
	return 0;
}
BWidget* BLayout::widget(int index) const
{
	if (index == 0)
	{
		return member.item.widget;
	}
	return 0;
}
BLayout* BLayout::layout(int index) const
{
	if (index == 0)
	{
		return member.item.layout;
	}
	return 0;
}
BSpacer* BLayout::spacer(int index) const
{
	if (index == 0)
	{
		return member.item.spacer;
	}
	return 0;
}
BSpring* BLayout::spring(int index) const
{
	if (index == 0)
	{
		return member.item.spring;
	}
	return 0;
}

BSize BLayout::baseSize() const
{
	return member.rect.size();
}
BSize BLayout::sizeHint() const
{
	if (member.item.dirty)
		member.item.flush();
	return member.item.sizeHint + this->margin().size();
}
bool BLayout::adjust()
{
	if (!layout_member(this)->dirty)
		return false;

	if (this->rect().empty() || member.item.empty())
		return false;

	const BRect& rect = this->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	const BMargin& margin = this->margin();
	int posX = x + margin.left();
	int posY = y + margin.top();
	int width = rect.width() - margin.width();
	int height = rect.height() - margin.height();

	if (member.item.prepare(height, width, height))
	{
		member.item.adjust(posX, posY, width, height);
	}

	layout_member(this)->dirty = false;
	if (this->checks(Signal_Adjust))
		this->emit(Signal_Adjust, layout_member(this)->rect);
	return true;
}

void BLayout::attached(BWidget* host)
{
	if (member.item.widget)
		member.host->addChild(member.item.widget);
	if (member.item.layout)
		member.item.layout->attached(host);
}
void BLayout::detached(BWidget* host)
{
	if (member.item.widget)
		host->removeChild(member.item.widget);
	if (member.item.layout)
		member.item.layout->detached(0);
}

void BLayout::event(const BEvent& event)
{
	BLayout* layout = member.item.layout;
	if (event.type() == Event_Style)
	{
		this->styleEvent(event);
		return;
	}
	if (event.type() == Event_Fresh)
	{
		this->freshEvent(event);
		return;
	}
	if (event.type() == Event_Paint)
	{
		this->paintEvent(event);
		return;
	}
}

void BLayout::styleEvent(const BEvent& event)
{
	const BStyle* style = event.value();

	style_member(style)->assign(Value_Margin_Left, member.margin.left());
	style_member(style)->assign(Value_Margin_Top, member.margin.top());
	style_member(style)->assign(Value_Margin_Right, member.margin.right());
	style_member(style)->assign(Value_Margin_Bottom, member.margin.bottom());
	style_member(style)->assign(Value_Width_Policy, member.sizePolicy.widthPolicy());
	style_member(style)->assign(Value_Height_Policy, member.sizePolicy.heightPolicy());

	this->dirty();
}
void BLayout::freshEvent(const BEvent& event)
{

}
void BLayout::paintEvent(const BEvent& event)
{

}

void BLayout::mouseEnterEvent(const BMouseEvent& mouseEvent)
{

}
void BLayout::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{

}

void BLayout::mousePressEvent(const BMouseEvent& mouseEvent)
{

}
void BLayout::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{

}
void BLayout::mouseMoveEvent(const BMouseEvent& mouseEvent)
{

}
